﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using H2.Streams;
using System.IO;

namespace Mystery
{
    public class NetgameEquipment
    {
        public const int Size = 144;
        public const int Offset = 288;

        public int Flags;
        public enum FlagsEnum
        {
            Levitate = 1,
            DestroyExistingOnNewSpawn = 2,
        }

        public short Gametype1;
        public short Gametype2;
        public short Gametype3;
        public short Gametype4;
        public enum GametypeEnum
        {
            None = 0,
            CaptureTheFlag = 1,
            Slayer = 2,
            Oddball = 3,
            KingOfTheHill = 4,
            Race = 5,
            Juggernaught = 7,
            Territories = 8,
            Assault = 9,
            AllTeamGameTypes = 11,
            AllGameTypes = 12,
            AllExceptCTF = 13,
        }

        public short TeamIndex;
        public short SpawnTimeSeconds;
        public short RespawnTimeSeconds;

        public short RespawnTimerStarts;
        public enum RespawnTimerStartsEnum
        {
            OnPickUp = 0,
            OnBodyDepletion = 1,
        }

        public int Classifier;
        public enum ClassifierEnum
        {
            Weapon = 0,
            PrimaryLightLand = 1,
            SecondaryLightLand = 2,
            PrimaryHeavyLand = 3,
            PrimaryFlying = 4,
            SeconaryHeavyLand = 5,
            PrimaryTurret = 6,
            SecondaryTurret = 7,
            Grenade = 8,
            Powerup = 9,
        }

        public byte[] Padding = new byte[40];

        public float PositionX;
        public float PositionY;
        public float PositionZ;

        public float OrientationYaw;
        public float OrientationPitch;
        public float OrientationRoll;

        public H2.DataTypes.Dependancy Collection;

        public byte[] Padding2 = new byte[48];

        public void ReadFrom(MapStream stream)
        {
            Flags = stream.ReadInt32();

            Gametype1 = stream.ReadInt16();
            Gametype2 = stream.ReadInt16();
            Gametype3 = stream.ReadInt16();
            Gametype4 = stream.ReadInt16();

            TeamIndex = stream.ReadInt16();
            SpawnTimeSeconds = stream.ReadInt16();
            RespawnTimeSeconds = stream.ReadInt16();

            RespawnTimerStarts = stream.ReadInt16();

            Classifier = stream.ReadInt32();

            Padding = stream.ReadBytes(Padding.Length);

            PositionX = stream.ReadFloat();
            PositionY = stream.ReadFloat();
            PositionZ = stream.ReadFloat();

            OrientationYaw = stream.ReadFloat();
            OrientationPitch = stream.ReadFloat();
            OrientationRoll = stream.ReadFloat();

            Collection = stream.ReadDependancy();

            Padding2 = stream.ReadBytes(Padding2.Length);
        }

        public void WriteTo(BaseStream stream)
        {
            stream.Write(Flags);

            stream.Write(Gametype1);
            stream.Write(Gametype2);
            stream.Write(Gametype3);
            stream.Write(Gametype4);

            stream.Write(TeamIndex);
            stream.Write(SpawnTimeSeconds);
            stream.Write(RespawnTimeSeconds);

            stream.Write(RespawnTimerStarts);

            stream.Write(Classifier);

            stream.Write(Padding);

            stream.Write(PositionX);
            stream.Write(PositionY);
            stream.Write(PositionZ);

            stream.Write(OrientationYaw);
            stream.Write(OrientationPitch);
            stream.Write(OrientationRoll);

            if (Collection != null)
                stream.Write(Collection.Class);
            else stream.Write("itmc");
            stream.Write(-1);

            stream.Write(Padding2);
        }

        public static void Generate(Scenario scnr, int selectedBSPIndex, bool ignoreScriptObjects, Placement[] placements, Palette[] palette, string itmcPrefix, MapStream.StatusChangeHandler statusChanged)
        {
            foreach (Placement p in placements)
            {
                if (ignoreScriptObjects && p.NameIndex >= 0) continue;
                if (selectedBSPIndex >= 0 && p.BSPIndex != selectedBSPIndex) continue;

                if (scnr.NetgameEquipment.Count >= 100)
                {
                    statusChanged("Netgame Equipment Limit Reached! Some Spawns Will Be Ignored");
                }

                Generate(p, scnr, palette, itmcPrefix, statusChanged);
            }
        }

        public static void Generate(Scenario scnr, int selectedBSPIndex, AISquad[] aiSquads, Palette[] vehiclePalette, string itmcPrefix, BaseStream.StatusChangeHandler statusChanged)
        {
            foreach (AISquad s in aiSquads)
            {
                if (s.Ignore) continue;
                if (s.VehicleIndex < 0) continue;
                //if (selectedBSPIndex >= 0 && s.BSPIndex != selectedBSPIndex) continue;

                if (scnr.NetgameEquipment.Count >= 100)
                {
                    statusChanged("Netgame Equipment Limit Reached! Some Spawns Will Be Ignored");
                }

                foreach (AISquad.StartingLocation sl in s.StartingLocations)
                {
                    NetgameEquipment ne = new NetgameEquipment();
                    scnr.NetgameEquipment.Add(ne);

                    ne.PositionX = sl.PositionX;
                    ne.PositionY = sl.PositionY;
                    ne.PositionZ = sl.PositionZ;
                    
                    ne.OrientationYaw = sl.RotationYaw;
                    ne.OrientationPitch = sl.RotationPitch;
                    ne.OrientationRoll = 0.0f;

                    ne.ChooseOptions(vehiclePalette[s.VehicleIndex], s.VehicleVariant.ToString().TrimEnd('\0'), itmcPrefix, statusChanged);
                }
            }
        }

        private static void Generate(Placement p, Scenario scnr, Palette[] palette, string itmcPrefix, BaseStream.StatusChangeHandler statusChanged)
        {
            if (p.TypeIndex < 0) return;

            NetgameEquipment ne = new NetgameEquipment();
            scnr.NetgameEquipment.Add(ne);

            ne.PositionX = p.PositionX;
            ne.PositionY = p.PositionY;
            ne.PositionZ = p.PositionZ;

            ne.OrientationYaw = p.RotationYaw;
            ne.OrientationPitch = p.RotationPitch;
            ne.OrientationRoll = p.RotationRoll;

            ne.ChooseOptions(palette[p.TypeIndex], p.VariantName.ToString().TrimEnd('\0'), itmcPrefix, statusChanged);
        }

        private void ChooseOptions(Palette p, string variant, string itmcPrefix, BaseStream.StatusChangeHandler statusChanged)
        {
            if (p.Item.ClassString == "weap") Flags = (int)FlagsEnum.Levitate;
            if (p.Item.ClassString == "vehi") Flags = (int)FlagsEnum.DestroyExistingOnNewSpawn;

            Gametype1 = (short)GametypeEnum.AllGameTypes;
            Gametype2 = (short)GametypeEnum.None;
            Gametype3 = (short)GametypeEnum.None;
            Gametype4 = (short)GametypeEnum.None;

            TeamIndex = 0;

            string itemCollectionName = Path.GetFileName(p.Item.Path) + ((variant.Length > 0) ? "_" : "") + variant;
            Collection = new H2.DataTypes.Dependancy();
            Collection.ClassString = "itmc";
            Collection.Path = itmcPrefix + itemCollectionName;

            switch (itemCollectionName)
            {
                case "frag_grenade":
                case "plasma_grenade":
                    Classifier = (int)ClassifierEnum.Grenade;
                    SpawnTimeSeconds = 30;
                    RespawnTimeSeconds = 0;
                    RespawnTimerStarts = (short)RespawnTimerStartsEnum.OnPickUp;
                    break;
                case "br_ammo":
                case "pistol_ammo":
                case "rocket_launcher_ammo":
                case "shotgun_ammo":
                case "sniper_ammo":
                case "smg_ammo":
                    Classifier = (int)ClassifierEnum.Powerup;
                    SpawnTimeSeconds = 60;
                    RespawnTimeSeconds = 0;
                    RespawnTimerStarts = (short)RespawnTimerStartsEnum.OnPickUp;
                    break;
                case "banshee":
                case "banshee_heretic":
                    Classifier = (int)ClassifierEnum.PrimaryFlying;
                    SpawnTimeSeconds = 60;
                    RespawnTimeSeconds = 120;
                    RespawnTimerStarts = (short)RespawnTimerStartsEnum.OnBodyDepletion;
                    break;
                case "scorpion":
                case "wraith":
                    Classifier = (int)ClassifierEnum.PrimaryHeavyLand;
                    SpawnTimeSeconds = 135;
                    RespawnTimeSeconds = 180;
                    RespawnTimerStarts = (short)RespawnTimerStartsEnum.OnBodyDepletion;
                    break;
                case "warthog":
                case "warthog_gauss":
                case "spectre":
                    Classifier = (int)ClassifierEnum.PrimaryLightLand;
                    SpawnTimeSeconds = 60;
                    RespawnTimeSeconds = 180;
                    RespawnTimerStarts = (short)RespawnTimerStartsEnum.OnBodyDepletion;
                    break;
                case "c_turret_ap":
                case "h_turret_ap":
                case "c_turret_mp":
                case "h_turret_mp":
                case "plasma_cannon":
                    Classifier = (int)ClassifierEnum.PrimaryTurret;
                    SpawnTimeSeconds = 350;
                    RespawnTimeSeconds = 0;
                    RespawnTimerStarts = (short)RespawnTimerStartsEnum.OnBodyDepletion;
                    break;
                //case "": //Unused
                //    Classifier = (int)ClassifierEnum.SeconaryHeavyLand;
                //    SpawnTimeSeconds = 90;
                //    RespawnTimeSeconds = 180;
                //    RespawnTimerStarts = (short)RespawnTimerStartsEnum.OnBodyDepletion;
                //    break;
                case "ghost":
                    Classifier = (int)ClassifierEnum.SecondaryLightLand;
                    SpawnTimeSeconds = 60;
                    RespawnTimeSeconds = 180;
                    RespawnTimerStarts = (short)RespawnTimerStartsEnum.OnBodyDepletion;
                    break;
                //case "": //Unused
                //    Classifier = (int)ClassifierEnum.SecondaryTurret;
                //    SpawnTimeSeconds;
                //    RespawnTimeSeconds;
                //    RespawnTimerStarts = (short)RespawnTimerStartsEnum.OnBodyDepletion;
                //    break;
                case "energy_blade":
                case "head_sp":
                    Classifier = (int)ClassifierEnum.Weapon; //Melee
                    SpawnTimeSeconds = 60;
                    RespawnTimeSeconds = 0;
                    RespawnTimerStarts = (short)RespawnTimerStartsEnum.OnBodyDepletion;
                    break;
                case "rocket_launcher":
                case "flak_cannon":
                case "phantom_turret_handheld":
                case "scarab_main_gun_handheld":
                    Classifier = (int)ClassifierEnum.Weapon; //Support High
                    SpawnTimeSeconds = 45;
                    RespawnTimeSeconds = 0;
                    RespawnTimerStarts = (short)RespawnTimerStartsEnum.OnBodyDepletion;
                    break;
                case "sniper_rifle":
                case "beam_rifle":
                    Classifier = (int)ClassifierEnum.Weapon; //Sniper
                    SpawnTimeSeconds = 30;
                    RespawnTimeSeconds = 0;
                    RespawnTimerStarts = (short)RespawnTimerStartsEnum.OnBodyDepletion;
                    break;
                case "brute_shot":
                case "sentinel_aggressor_beam":
                case "sentinel_aggressor_welder":
                case "sent_agg_beam_elim":
                    Classifier = (int)ClassifierEnum.Weapon; //Support Low
                    SpawnTimeSeconds = 15;
                    RespawnTimeSeconds = 0;
                    RespawnTimerStarts = (short)RespawnTimerStartsEnum.OnBodyDepletion;
                    break;
                case "battle_rifle":
                case "plasma_rifle":
                case "covenant_carbine":
                    Classifier = (int)ClassifierEnum.Weapon; //Rifle
                    SpawnTimeSeconds = 45;
                    RespawnTimeSeconds = 0;
                    RespawnTimerStarts = (short)RespawnTimerStartsEnum.OnPickUp;
                    break;
                case "plasma_pistol":
                case "magnum":
                case "needler":
                case "smg":
                case "shotgun":
                    Classifier = (int)ClassifierEnum.Weapon; //Pistol
                    SpawnTimeSeconds = 30;
                    RespawnTimeSeconds = 0;
                    RespawnTimerStarts = (short)RespawnTimerStartsEnum.OnPickUp;
                    break;
                case "pelican":
                case "pelican_unarmed":
                case "pelican_attack":
                case "phantom":
                    Classifier = (int)ClassifierEnum.SeconaryHeavyLand; //Heavy Flyers
                    SpawnTimeSeconds = 0;
                    RespawnTimeSeconds = 0;
                    RespawnTimerStarts = (short)RespawnTimerStartsEnum.OnBodyDepletion;
                    break;
                default:
                    statusChanged("Unassigned Netgame Equipment: " + itemCollectionName);
                    Classifier = (int)ClassifierEnum.SeconaryHeavyLand; //Others
                    SpawnTimeSeconds = 0;
                    RespawnTimeSeconds = 0;
                    RespawnTimerStarts = (short)RespawnTimerStartsEnum.OnBodyDepletion;
                    break;
            }
        }
    }
}
